/*
* ieee1394io.cc -- asynchronously grabbing DV data
* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
* Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
* Copyright (C) 2007 Stéphane Brunner <stephane.brunner@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/**
    \page The IEEE 1394 Reader Class
 
    This text explains how the IEEE 1394 Reader class works.
 
    The IEEE1394Reader object maintains a connection to a DV
    camcorder. It reads DV frames from the camcorder and stores them
    in a queue. The frames can then be retrieved from the buffer and
    displayed, stored, or processed in other ways.
 
    The IEEE1394Reader class supports asynchronous operation: it
    starts a separate thread, which reads as fast as possible from the
    ieee1394 interface card to make sure that no frames are
    lost. Since the buffer can be configured to hold many frames, no
    frames will be lost even if the disk access is temporarily slow.
 
    There are two queues available in an IEEE1394Reader object. One
    queue holds empty frames, the other holds frames filled with DV
    content just read from the interface. During operation the reader
    thread takes unused frames from the inFrames queue, fills them and
    places them in the outFrame queue. The program can then take
    frames from the outFrames queue, process them and finally put
    them back in the inFrames queue.
 
 */

#include <deque>
#include <iostream>

using std::cerr;
using std::endl;

#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

#include <libavc1394/avc1394.h>
#include <libavc1394/avc1394_vcr.h>
#include <libavc1394/rom1394.h>

#include "ieee1394io.h"
#include "page_capture.h"
#include "frame.h"
#include "error.h"

IEEE1394::IEEE1394() {
	for ( int i = 0; i < BUFFERED_FRAMES; i++ )
	{
		frames[i] = NULL;
	}
}

IEEE1394::~IEEE1394() {
	for ( int i = 0; i < BUFFERED_FRAMES; i++ )
	{
		if (frames[i] != NULL) {
			delete frames[i];
		}
	}
}

/** Initializes the IEEE1394Reader object.
 
    The object is initialized with port and channel number. These
    parameters define the interface card and the iso channel on which
    the camcorder sends its data.
 
    The object contains a list of empty frames, which are allocated
    here. 50 frames (2 seconds) should be enough in most cases.
 
    \param c the iso channel number to use
    \param bufSize the number of frames to allocate for the frames buffer
 */


IEEE1394Reader::IEEE1394Reader( int c, int bufSize ) :
	channel( c ),
	isRunning( false )
{
	/* Initialize mutexes */
	pthread_mutex_init( &mutex, NULL );

	/* Initialise mutex and condition for action triggerring */
	pthread_mutex_init( &condition_mutex, NULL );
	pthread_cond_init( &condition, NULL );
}


/** Destroys the IEEE1394Reader object.
 
    In particular, it deletes all frames in the inFrames and outFrames
    queues, as well as the one currently in use.  Note that one or
    more frames may have been taken out of the queues by a user of the
    IEEE1394Reader class.
 
*/

IEEE1394Reader::~IEEE1394Reader()
{
	Frame * frame;

//	for ( int i = outFrames.size(); i > 0; --i )
//	{
//		frame = outFrames[ 0 ];
//		outFrames.pop_front();
//		DoneWithFrame( frame );
//	}
	pthread_mutex_destroy( &condition_mutex );
	pthread_cond_destroy( &condition );
}

/** Fetches the next frame from the output queue
 
    The outFrames contains a list of frames to be processed (saved,
    displayed) by the user of this class.  Copy the first frame
    (actually only a pointer to it) and remove it from the queue.
 
    \note If this returns NULL, wait some time (1/25 sec.) before
    calling it again.
 
    \return a pointer to the current frame, or NULL if no frames are
    in the queue
 
 */

Frame* IEEE1394Reader::GetFrame()
{
/*	Frame * frame = NULL;

	pthread_mutex_lock( &mutex );

	if ( frames.size() > 0 )
	{
		frame = frames[ 0 ];
		frames.pop_front();
	}
	pthread_mutex_unlock( &mutex );
	if ( frame != NULL ) {
		frame->ExtractHeader();
	}

	return frame;*/
	return frames[framePosition];
}


/*bool IEEE1394Reader::WaitForAction( int seconds )
{
	pthread_mutex_lock( &mutex );
//	int size = frames.size( );
	pthread_mutex_unlock( &mutex );

	if ( size == 0 )
	{
		pthread_mutex_lock( &condition_mutex );
		if ( seconds == 0 )
		{
			pthread_cond_wait( &condition, &condition_mutex );
			pthread_mutex_unlock( &condition_mutex );
			pthread_mutex_lock( &mutex );
//			size = frames.size( );
		}
		else
		{
			struct timeval tp;
			struct timespec ts;
			int result;

			gettimeofday( &tp, NULL );
			ts.tv_sec = tp.tv_sec + seconds;
			ts.tv_nsec = tp.tv_usec * 1000;

			result = pthread_cond_timedwait( &condition, &condition_mutex, &ts );
			pthread_mutex_unlock( &condition_mutex );
			pthread_mutex_lock( &mutex );

			if ( result == ETIMEDOUT )
				size = 0;
			else
				size = frames.size();
		}
		pthread_mutex_unlock( &mutex );
	}

	return size != 0;
}*/


void IEEE1394Reader::TriggerAction( )
{
	pthread_mutex_lock( &condition_mutex );
	pthread_cond_signal( &condition );
	pthread_mutex_unlock( &condition_mutex );
}


#ifdef HAVE_IEC61883
/** Initializes the raw1394Reader object.
 
    The object is initialized with port and channel number. These
    parameters define the interface card and the iso channel on which
    the camcorder sends its data.
 
    \param c the iso channel number to use
    \param bufSize the number of frames to allocate for the frames buffer
 */


iec61883Reader::iec61883Reader( int c, int bufSize ) :
	IEEE1394Reader( c, bufSize )
{
	m_handle = NULL;
	cerr << ">>> Using iec61883 capture" << endl;
}


iec61883Reader::~iec61883Reader()
{
	StopThread();
}


/** Start receiving DV frames
 
    The ieee1394 subsystem is initialized with the parameters provided
    to the constructor (port and channel).  The received frames can be
    retrieved from the outFrames queue.
 
*/

bool iec61883Reader::StartThread( int port )
{
cerr<<"iec61883Reader::StartThread"<<endl;
	if ( isRunning )
		return true;
	if ( port < 0 )
		return false;
	cerr << ">>> iec61883Reader::StartThread on port " << port << endl;

	pthread_mutex_lock( &mutex );
	if ( Open( port ) && StartReceive() )
	{
		isRunning = true;
		pthread_create( &thread, NULL, ThreadProxy, this );
		pthread_mutex_unlock( &mutex );
		return true;
	}
	else
	{
		Close();
		pthread_mutex_unlock( &mutex );
		return false;
	}
}


/** Stop the receiver thread.
 
    The receiver thread is being canceled. It will finish the next
    time it calls the pthread_testcancel() function.  After it is
    canceled, we turn off iso receive and close the ieee1394
    subsystem.  We also remove all frames in the outFrames queue that
    have not been processed until now.
 
*/

void iec61883Reader::StopThread()
{
cerr<<"iec61883Reader::StopThread"<<endl;
	if ( isRunning )
	{
		isRunning = false;
		pthread_join( thread, NULL );
	}
	Close();
}


void iec61883Reader::ResetHandler( void )
{
cerr<<"iec61883Reader::ResetHandler"<<endl;
}

int iec61883Reader::ResetHandlerProxy( raw1394handle_t handle, unsigned int generation )
{
cerr<<"iec61883Reader::ResetHandlerProxy"<<endl;
	iec61883_dv_t dv = static_cast< iec61883_dv_t >( raw1394_get_userdata( handle ) );
	iec61883_dv_fb_t dvfb = static_cast< iec61883_dv_fb_t >( iec61883_dv_get_callback_data( dv ) );
	iec61883Reader *self = static_cast< iec61883Reader* >( iec61883_dv_fb_get_callback_data( dvfb ) );
	raw1394_update_generation( handle, generation );
	if ( self )
		self->ResetHandler();
	return 0;
}


/** Open the raw1394 interface
 
    \return success/failure
*/

bool iec61883Reader::Open( int port )
{
cerr<<"iec61883Reader::Open"<<endl;
	bool success;

	assert( !m_handle );

	try
	{
		m_handle = raw1394_new_handle_on_port( port );
		if ( !m_handle )
			return false;
		raw1394_set_bus_reset_handler( m_handle, this->ResetHandlerProxy );

		m_iec61883dv = iec61883_dv_fb_init( m_handle, HandlerProxy, this );
		success = ( m_iec61883dv != NULL );
	}
	catch ( string exc )
	{
		Close();
		cerr << exc << endl;
		success = false;
	}
	return success;
}


/** Close the raw1394 interface
 
*/

void iec61883Reader::Close()
{
cerr<<"iec61883Reader::Close"<<endl;
	if ( m_handle )
	{
		StopReceive();
		raw1394_destroy_handle( m_handle );
		m_handle = NULL;
	}
}

bool iec61883Reader::StartReceive()
{
cerr<<"iec61883Reader::StartReceive"<<endl;
	bool success;

	/* Starting iso receive */
	try
	{
		fail_neg( iec61883_dv_fb_start( m_iec61883dv, channel ) );
		success = true;
	}
	catch ( string exc )
	{
		cerr << exc << endl;
		success = false;
	}
	return success;
}


void iec61883Reader::StopReceive()
{
cerr<<"iec61883Reader::StopReceive"<<endl;
	if ( m_iec61883dv )
	{
		iec61883_dv_fb_close( m_iec61883dv );
		m_iec61883dv = NULL;
	}
}

int iec61883Reader::HandlerProxy( unsigned char *data, int length, int complete, 
	void *callback_data )
{
	iec61883Reader *self = static_cast< iec61883Reader* >( callback_data );
	return self->Handler( length, complete, data );
}

int iec61883Reader::Handler( int length, int complete, unsigned char *data )
{
	pthread_mutex_lock( &mutex );
//	Frame *frame = new Frame();
//	frames.push_back( frame );
	int newPos = (framePosition+1)%BUFFERED_FRAMES;
	if (frames[newPos] == NULL) {
		frames[newPos] = new Frame();
	}
	Frame *frame = frames[newPos];
	frame->bytesInFrame = 0;

	memcpy( frame->data, data, length );
	frame->bytesInFrame = length;
	framePosition = newPos;
	pthread_mutex_unlock( &mutex );
	TriggerAction();

	return 0;
}


/** 
    The thread responsible for polling the raw1394 interface.
 
    Though this is an infinite loop, it can be canceled by StopThread,
    but only in the pthread_testcancel() function.
*/
void* iec61883Reader::ThreadProxy( void* arg )
{
	iec61883Reader* self = static_cast< iec61883Reader* >( arg );
	return self->Thread();
}

void* iec61883Reader::Thread()
{
cerr<<"iec61883Reader::Thread"<<endl;
	struct pollfd raw1394_poll;
	int result;

	assert( m_handle );
	raw1394_poll.fd = raw1394_get_fd( m_handle );
	raw1394_poll.events = POLLIN | POLLERR | POLLHUP | POLLPRI;

	while ( isRunning )
	{
		while ( ( result = poll( &raw1394_poll, 1, 200 ) ) < 0 )
		{
			if ( !( errno == EAGAIN || errno == EINTR ) )
			{
				perror( "error: raw1394 poll" );
				break;
			}
		}
		if ( result > 0 && ((raw1394_poll.revents & POLLIN) || (raw1394_poll.revents & POLLPRI))) {
//cerr<<111<<endl;
//cerr<<(int)m_handle<<endl;
			result = raw1394_loop_iterate(m_handle);
//cerr<<"fg"<<endl;
		}
	}
cerr<<"iec61883Reader::Thread - end"<<endl;
	return NULL;
}
#endif


#ifdef HAVE_DV1394
/** Initializes the dv1394Reader object.
 
    \param c the iso channel number to use
    \param bufSize the number of frames to allocate for the frames buffer
 */


dv1394Reader::dv1394Reader( int c, int bufSize ) :
		IEEE1394Reader( c, bufSize )
{
	m_dv1394_map = NULL;
	m_dv1394_fd = -1;
	cerr << ">>> Using dv1394 capture" << endl;
}


dv1394Reader::~dv1394Reader()
{}


/** Start receiving DV frames
 
    The ieee1394 subsystem is initialized with the parameters provided
    to the constructor (port and channel).  The received frames can be
    retrieved from the outFrames queue.
 
*/

bool dv1394Reader::StartThread( int port )
{
cerr<<"dv1394Reader::StartThread"<<endl;
	if ( isRunning )
		return true;
	pthread_mutex_lock( &mutex );
	if ( Open( port ) && StartReceive() )
	{
		isRunning = true;
		pthread_create( &thread, NULL, ThreadProxy, this );
		pthread_mutex_unlock( &mutex );
		return true;
	}
	else
	{
		Close();
		pthread_mutex_unlock( &mutex );
		return false;
	}
}


/** Stop the receiver thread.
 
    The receiver thread is being canceled. It will finish the next
    time it calls the pthread_testcancel() function.  After it is
    canceled, we turn off iso receive and close the ieee1394
    subsystem.  We also remove all frames in the outFrames queue that
    have not been processed until now.
*/

void dv1394Reader::StopThread()
{
cerr<<"dv1394Reader::StopThread"<<endl;
	if ( isRunning )
	{
		isRunning = false;
		pthread_join( thread, NULL );
		StopReceive();
		Close();
	}
	TriggerAction( );
}


/** Open the dv1394 interface
 
    \return success/failure
*/

bool dv1394Reader::Open( int port )
{
cerr<<"dv1394Reader::Open"<<endl;
	int n_frames = DV1394_MAX_FRAMES / 4;
	struct dv1394_init init =
	    {
		    DV1394_API_VERSION, channel, n_frames, DV1394_PAL, 0, 0, 0
	    };

	m_dv1394_fd = open( "/dev/dv1394/0", O_RDWR );
	if ( m_dv1394_fd == -1 )
		return false;

	if ( ioctl( m_dv1394_fd, DV1394_INIT, &init ) )
	{
		perror( "dv1394 INIT ioctl" );
		close( m_dv1394_fd );
		m_dv1394_fd = -1;
		return false;
	}

	m_dv1394_map = ( unsigned char * ) mmap( NULL, DV1394_PAL_FRAME_SIZE * n_frames,
	               PROT_READ | PROT_WRITE, MAP_SHARED, m_dv1394_fd, 0 );
	if ( m_dv1394_map == MAP_FAILED )
	{
		perror( "mmap frame buffers" );
		close( m_dv1394_fd );
		m_dv1394_fd = -1;
		m_dv1394_map = NULL;
		return false;
	}

	return true;
}


/** Close the dv1394 interface
 
*/
void dv1394Reader::Close()
{
cerr<<"dv1394Reader::Close"<<endl;
	if ( m_dv1394_fd != -1 )
	{
		if ( m_dv1394_map != NULL )
			munmap( m_dv1394_map, DV1394_PAL_FRAME_SIZE * DV1394_MAX_FRAMES / 4 );
		close( m_dv1394_fd );
		m_dv1394_map = NULL;
		m_dv1394_fd = -1;
	}
}


bool dv1394Reader::StartReceive()
{
cerr<<"dv1394Reader::StartReceive"<<endl;
	/* Starting iso receive */
	if ( ioctl( m_dv1394_fd, DV1394_START_RECEIVE, NULL ) )
	{
		perror( "dv1394 START_RECEIVE ioctl" );
		return false;
	}
	return true;
}


void dv1394Reader::StopReceive()
{
cerr<<"dv1394Reader::StopReceive"<<endl;
}

bool dv1394Reader::Handler( int handle )
{
cerr<<"dv1394Reader::Handler"<<endl;
	struct dv1394_status dvst;
	struct pollfd pol;
	int result;
		
	pol.fd = m_dv1394_fd;
	pol.events = POLLIN | POLLERR | POLLHUP;
	while ( ( result = poll( &pol, 1, 200 ) ) < 0 )
	{
		if ( !( errno == EAGAIN || errno == EINTR ) )
		{
			perror( "error: dv1394 poll" );
			return false;
		}
	}
	if ( result == 0 )
		return true;

	if ( ioctl( handle, DV1394_GET_STATUS, &dvst ) )
	{
		perror( "ioctl GET_STATUS" );
		return false;
	}

	return true;
}


/** The thread responsible for polling the dv1394 interface.
 
    Though this is an infinite loop, it can be canceled by StopThread,
    but only in the pthread_testcancel() function.
 
*/
void* dv1394Reader::ThreadProxy( void* arg )
{
cerr<<"dv1394Reader::ThreadProxy"<<endl;
	dv1394Reader* self = static_cast< dv1394Reader* >( arg );
	return self->Thread();
}

void* dv1394Reader::Thread( )
{
cerr<<"dv1394Reader::Thread"<<endl;

	while ( isRunning )
	{
		if ( ! Handler( m_dv1394_fd ) )
			break;
	}
	return NULL;
}
#endif


/** Initializes the AVC object.
 
    \param p the number of the interface card to use (port)
 */


AVC::AVC( void ) : port( -1 ), totalPorts( 0 )
{
	pthread_mutex_init( &avc_mutex, NULL );
	avc_handle = NULL;
	struct raw1394_portinfo pinf[ 16 ];

	try
	{
		avc_handle = raw1394_new_handle();
		if ( avc_handle == 0 )
			return;
		fail_neg( totalPorts = raw1394_get_port_info( avc_handle, pinf, 16 ) );
		raw1394_destroy_handle( avc_handle );
		avc_handle = NULL;
	}
	catch ( string exc )
	{
		if ( avc_handle != NULL )
			raw1394_destroy_handle( avc_handle );
		avc_handle = NULL;
		cerr << exc << endl;
	}
	return;
}


/** Destroys the AVC object.
 
*/

AVC::~AVC()
{
	if ( avc_handle != NULL )
	{
		pthread_mutex_lock( &avc_mutex );
		raw1394_destroy_handle( avc_handle );
		avc_handle = NULL;
		pthread_mutex_unlock( &avc_mutex );
	}
}

int AVC::ResetHandler( raw1394handle_t handle, unsigned int generation )
{
	cerr << "Reset Handler received" << endl;
	raw1394_update_generation( handle, generation );
	return 0;
}


/** See if a node_id is still valid and pointing to an AV/C Recorder.
 
	If the node_id is not valid, then look for the first AV/C device on
	the bus;
	
	\param phyID The node_id to check.
	\return The same node_id if valid, a new node_id if not valid and a
	        another AV/C recorder exists, or -1 if not valid and no
			AV/C recorders exist.
   
*/
int AVC::isPhyIDValid( int phyID )
{
cerr<<"AVC::isPhyIDValid"<<endl;
	int value = -1;
	int currentNode, nodeCount;
	rom1394_directory rom1394_dir;

	pthread_mutex_lock( &avc_mutex );
	if ( avc_handle != NULL )
	{
		nodeCount = raw1394_get_nodecount( avc_handle );
		if ( phyID >= 0 && phyID < nodeCount )
		{
			if ( rom1394_get_directory( avc_handle, phyID, &rom1394_dir ) >= 0 )
			{
				if ( rom1394_get_node_type( &rom1394_dir ) == ROM1394_NODE_TYPE_AVC )
				{
					if ( avc1394_check_subunit_type( avc_handle, phyID, AVC1394_SUBUNIT_TYPE_VCR ) )
						value = phyID;
				}
				rom1394_free_directory( &rom1394_dir );
			}
		}

		if ( value == -1 )
		{
			raw1394_destroy_handle( avc_handle );
			avc_handle = NULL;
			port = -1;
		}
	}
	for ( int p = 0; value == -1 && p < totalPorts; p++ )
	{
		if ( ( avc_handle = raw1394_new_handle_on_port( p ) ) )
		{
			port = p;
			raw1394_set_bus_reset_handler( avc_handle, this->ResetHandler );

			// look for a new AVC recorder
			nodeCount = raw1394_get_nodecount( avc_handle );
			for ( currentNode = 0; value == -1 && currentNode < nodeCount; currentNode++ )
			{
				if ( rom1394_get_directory( avc_handle, currentNode, &rom1394_dir ) >= 0 )
				{
					if ( rom1394_get_node_type( &rom1394_dir ) == ROM1394_NODE_TYPE_AVC )
					{
						if ( avc1394_check_subunit_type( avc_handle, currentNode, AVC1394_SUBUNIT_TYPE_VCR ) )
						{
							// set Preferences to the newly found AVC node and return
							octlet_t guid = rom1394_get_guid( avc_handle, currentNode );
							value = currentNode;
						}
					}
					rom1394_free_directory( &rom1394_dir );
				}
			}
			if ( value == -1 )
			{
				raw1394_destroy_handle( avc_handle );
				avc_handle = NULL;
			}
		}
	}
	if ( value == -1 )
		port = -1;
	pthread_mutex_unlock( &avc_mutex );
	return value;
}

unsigned int AVC::TransportStatus( int phyID )
{
cerr<<"AVC::TransportStatus"<<endl;
	quadlet_t val = 0;
	pthread_mutex_lock( &avc_mutex );
	if ( avc_handle != NULL )
	{
		if ( phyID >= 0 )
			val = avc1394_vcr_status( avc_handle, phyID );
	}
	pthread_mutex_unlock( &avc_mutex );
	return val;
}

int AVC::getNodeId( const char *guid )
{
cerr<<"AVC::getNodeId"<<endl;
	int value = -1;
	pthread_mutex_lock( &avc_mutex );
	if ( avc_handle != NULL )
	{
		raw1394_destroy_handle( avc_handle );
		avc_handle = NULL;
	}
	port = 0;
	for ( int p = 0; value == -1 && p < totalPorts; p++ )
	{
		rom1394_directory rom1394_dir;
		
		if ( ( avc_handle = raw1394_new_handle_on_port( p ) ) )
		{
			int nodeCount = raw1394_get_nodecount( avc_handle );

			port = p;
			raw1394_set_bus_reset_handler( avc_handle, this->ResetHandler );

			for ( int currentNode = 0; value == -1 && currentNode < nodeCount; currentNode++ )
			{
				if ( rom1394_get_directory( avc_handle, currentNode, &rom1394_dir ) >= 0 )
				{
					octlet_t currentGUID = rom1394_get_guid( avc_handle, currentNode );
					char currentGUIDStr[ 65 ];
					snprintf( currentGUIDStr, 64, "%08x%08x", ( quadlet_t ) ( currentGUID >> 32 ),
						( quadlet_t ) ( currentGUID & 0xffffffff ) );
					if ( strncmp( currentGUIDStr, guid, 64 ) == 0 )
						value = currentNode;
					rom1394_free_directory( &rom1394_dir );
				}
			}
			if ( value == -1 )
			{
				raw1394_destroy_handle( avc_handle );
				avc_handle = NULL;
			}
		}
	}
	if ( value == -1 )
		port = -1;
	pthread_mutex_unlock( &avc_mutex );
	return value;
}


IEEE1394Writer::IEEE1394Writer() :
		m_isInitialised( false ), m_isRunning( false )
{
	pthread_mutex_init( &m_dequeMutex, NULL );
	pthread_mutex_init( &m_conditionMutex, NULL );
	pthread_cond_init( &m_condition, NULL );
}

IEEE1394Writer::~IEEE1394Writer()
{
	for ( int i = m_deque.size(); i > 0; --i )
	{
		Frame* frame = m_deque[ 0 ];
		m_deque.pop_front();
//		DoneWithFrame( frame );
		int newPos = (framePosition+1)%BUFFERED_FRAMES;
		if (frames[newPos] != NULL) {
			delete frames[newPos];
		}
		frames[newPos] = frame;
		framePosition = newPos;
	}
	pthread_mutex_destroy( &m_conditionMutex );
	pthread_cond_destroy( &m_condition );
	pthread_mutex_destroy( &m_dequeMutex );
}

/*Frame *IEEE1394Writer::InternalGetFrame()
{
	Frame * frame;
	if ( frames.begin() == frames.end() )
	{
		frame = new Frame( );
	}
	else
	{
		frame = frames[ 0 ];
		frames.pop_front( );
	}
	return frame;
}*/

void IEEE1394Writer::StartThread()
{
	if ( m_isRunning )
		return;
	pthread_create( &m_thread, NULL, ThreadProxy, this );
}

void IEEE1394Writer::StopThread()
{
	if ( m_isRunning )
	{
		m_isRunning = false;
		pthread_join( m_thread, NULL );
	}
}

int IEEE1394Writer::WaitForAction( bool isBlocking, int seconds )
{
	pthread_mutex_lock( &m_dequeMutex );
	int size = m_deque.size( );
	pthread_mutex_unlock( &m_dequeMutex );

	if ( ( unsigned int )size >= m_nBuffers )
	{
		if ( isBlocking )
		{
			pthread_mutex_lock( &m_conditionMutex );
			if ( seconds == 0 )
			{
				pthread_cond_wait( &m_condition, &m_conditionMutex );
				pthread_mutex_unlock( &m_conditionMutex );
				pthread_mutex_lock( &m_dequeMutex );
				size = m_deque.size( );
			}
			else
			{
				struct timeval tp;
				struct timespec ts;
				int result;
	
				gettimeofday( &tp, NULL );
				ts.tv_sec = tp.tv_sec + seconds;
				ts.tv_nsec = tp.tv_usec * 1000;
	
				result = pthread_cond_timedwait( &m_condition, &m_conditionMutex, &ts );
				pthread_mutex_unlock( &m_conditionMutex );
				pthread_mutex_lock( &m_dequeMutex );
	
				size = m_deque.size();
				if ( ( unsigned int )size >= m_nBuffers )
					size = -size;
			}
			pthread_mutex_unlock( &m_dequeMutex );
		}
		else
		{
			size = -size;
		}
	}

	return size;
}

void IEEE1394Writer::TriggerAction( )
{
	pthread_mutex_lock( &m_conditionMutex );
	pthread_cond_signal( &m_condition );
	pthread_mutex_unlock( &m_conditionMutex );
}

void* IEEE1394Writer::ThreadProxy( void *arg )
{
	IEEE1394Writer* self = static_cast< IEEE1394Writer* >( arg );
	return self->Thread();
}


#ifdef HAVE_IEC61883
iec61883Writer::iec61883Writer( int port, unsigned int channel, unsigned int buffers )
	: m_port( port ), m_handle( NULL ), m_iec61883dv( NULL ),
	m_data( 0 ), m_index( -1 ), m_isSilent( false ), m_isReset( false )
{
	cerr << ">>> iec61883Writer::iec61883Writer port " << port << " channel " << channel << endl;
	m_channel = channel;
	m_nBuffers = buffers;
}

iec61883Writer::~iec61883Writer()
{
	// signal SendFrame to not accept incoming frame
	TriggerAction();
	m_isReset = true;

	StopThread();

	if ( m_iec61883dv )
	{
		iec61883_dv_close( m_iec61883dv );
		raw1394_destroy_handle( m_handle );
	}
}


int iec61883Writer::HandlerProxy( unsigned char *data, int n_dif_blocks,
	unsigned int dropped, void *callback_data)
{
	if ( callback_data )
	{
		iec61883Writer* writer = static_cast< iec61883Writer* >( callback_data );
		return writer->Handler( data, n_dif_blocks, dropped );
	}
	else
	{
		return -1;
	}
}


int iec61883Writer::Handler( unsigned char *data, int n_dif_blocks, unsigned int dropped )
{
cerr<<">>> iec61883Writer::Handler"<<endl;
	// Initialize or reset current frame
	if ( m_index <= 0 )
	{
		// Cancel thread by telling raw1394_iso to end
		// It is important for reliability of next transmission in the same
		// process that this occurs on a frame boundary!
		if ( !m_isRunning || m_isReset )
			return -1;

		pthread_mutex_lock( &m_dequeMutex );
		Frame* frame = m_deque[0];
		pthread_mutex_unlock( &m_dequeMutex );

		m_data = frame->data;
		m_index = frame->GetFrameSize();
	}

	// Transfer the DV data to FireWire
	memcpy( data, m_data, n_dif_blocks * 480 );

	// Move data cursor
	m_data += n_dif_blocks * 480;
	m_index -= n_dif_blocks * 480;

	// When we have sent all of the current frame
	if ( m_index == 0 )
	{
		pthread_mutex_lock( &m_dequeMutex );
		Frame* frame = m_deque[0];
		if ( m_deque.size() > 1 )
		{
			// Discard the current frame only if more than one available
			m_deque.pop_front();
			
//			DoneWithFrame( frame );
			int newPos = (framePosition+1)%BUFFERED_FRAMES;
			if (frames[newPos] != NULL) {
				delete frames[newPos];
			}
			frames[newPos] = frame;
			framePosition = newPos;

			// Awaken a blocked parent thread
			TriggerAction();

			// frontmost frame has changed so we need to silence it if repeated
			m_isSilent = false;
		}
		else if ( !m_isSilent )
		{
			AudioInfo info;
			int16_t* silence[4];

			// Make the repeated frame silent to avoid annoyance
			for ( int i = 0; i < 4; i++ )
				silence[i] = (int16_t*) calloc( 2 * DV_AUDIO_MAX_SAMPLES, sizeof( int16_t ) );
			info.channels = 2;
			for ( int i = 0; i < 4; i++ )
				free( silence[i] );

			// Do not need to silence this frame again
			m_isSilent = true;
		}
		pthread_mutex_unlock( &m_dequeMutex );
	}

	return 0;
}

bool iec61883Writer::Open( bool isPAL )
{
	assert( m_handle == 0 );
	m_isInitialised = false;
	m_handle = raw1394_new_handle_on_port( m_port );
	if ( m_handle != NULL )
	{
		raw1394_set_bus_reset_handler( m_handle, this->ResetHandlerProxy );
		m_iec61883dv = iec61883_dv_xmit_init( m_handle, isPAL, HandlerProxy, this );
		m_isInitialised = ( m_iec61883dv != NULL );
		//iec61883_dv_set_synch( m_iec61883dv, 1 ); this is causing hang on close
	}

	return m_isInitialised;
}

bool iec61883Writer::SendFrame( Frame &frame, bool isBlocking )
{
	bool result = false;

	if ( m_isReset )
	{
		StopThread();
		if ( m_iec61883dv )
		{
 			// iec61883_dv_close( m_iec61883dv ) hangs here
			free( m_iec61883dv );
			m_iec61883dv = NULL;
		}
		raw1394_destroy_handle( m_handle );
		m_handle = NULL;
		m_isInitialised = m_isReset = false;
	}

	// Block if too full
	if ( WaitForAction( isBlocking ) >= 0 )
	{
		// Duplicate the frame
//		Frame *newFrame = InternalGetFrame();
//		*newFrame = frame;
	
		pthread_mutex_lock( &m_dequeMutex );
//		m_deque.push_back( newFrame );
		int newPos = (framePosition+1)%BUFFERED_FRAMES;
		if (frames[newPos] != NULL) {
			delete frames[newPos];
		}
		frames[newPos] = &frame;
		framePosition = newPos;
		pthread_mutex_unlock( &m_dequeMutex );
	
		if ( !m_isInitialised && Open( frame.IsPAL() ) )
		{
			m_isInitialised = ( iec61883_dv_xmit_start( m_iec61883dv, m_channel ) == 0 );
			StartThread();
		}
		result = true;
	}
	return result;
}

void* iec61883Writer::Thread()
{
	// poll raw1394
	struct pollfd pfd = {
		fd: raw1394_get_fd( m_handle ),
		events: POLLIN,
		revents: 0
	};
	int result = 0;

	m_isRunning = true;
	while ( !m_isReset && result == 0 )
	{
		if ( poll( &pfd, 1, 100 ) > 0 && ( pfd.revents & POLLIN ) )
			result = raw1394_loop_iterate( m_handle );
	}

	return NULL;
}

void iec61883Writer::ResetHandler( void )
{
	cerr << ">>> iec61883Writer::ResetHandler" << endl;
	if ( m_isInitialised )
		m_isReset = true;
}

int iec61883Writer::ResetHandlerProxy( raw1394handle_t handle, unsigned int generation )
{
	iec61883_dv_t dv = static_cast< iec61883_dv_t >( raw1394_get_userdata( handle ) );
	iec61883Writer *self = static_cast< iec61883Writer* >( iec61883_dv_get_callback_data( dv ) );
	raw1394_update_generation( handle, generation );
	if ( self )
		self->ResetHandler();
	return 0;
}
#endif


#ifdef HAVE_DV1394
dv1394Writer::dv1394Writer( string device, unsigned int channel, unsigned int nBuffers,
                            unsigned int cip_n, unsigned int cip_d, unsigned int syt_offset )
	: m_channel( channel ), m_cip_n( cip_n ), m_cip_d( cip_d ), m_syt_offset( syt_offset ),
	  m_isSilent( false )
{
	m_nBuffers = ( nBuffers > DV1394_MAX_FRAMES ) ? DV1394_MAX_FRAMES : nBuffers;
	m_fd = open( device.c_str(), O_RDWR );
	cerr << ">>> dv1394Writer::dv1394Writer " << device << " channel " << m_channel << " fd " << m_fd << endl;
}


dv1394Writer::~dv1394Writer()
{
	StopThread();
	if ( m_fd != -1 )
	{
		close( m_fd );
		m_fd = -1;
	}
}


bool dv1394Writer::SendFrame( Frame &frame, bool isBlocking )
{
	bool result = false;

	if ( m_fd < 0 )
		return result;

	bool isPAL = frame.IsPAL();

	// Block if too full
	if ( WaitForAction( isBlocking ) >= 0 )
	{
		// Duplicate the frame
//		Frame *newFrame = InternalGetFrame();
//		*newFrame = frame;
	
		pthread_mutex_lock( &m_dequeMutex );
//		m_deque.push_back( newFrame );
		int newPos = (framePosition+1)%BUFFERED_FRAMES;
		if (frames[newPos] != NULL) {
			delete frames[newPos];
		}
		frames[newPos] = &frame;
		framePosition = newPos;
		pthread_mutex_unlock( &m_dequeMutex );
	
		if ( !m_isInitialised )
		{
			m_isInitialised = true;
	
			struct dv1394_init setup =
				{
				api_version: DV1394_API_VERSION,
				channel: m_channel,
				n_frames: m_nBuffers,
				format: ( isPAL ? DV1394_PAL : DV1394_NTSC ),
				cip_n : m_cip_n,
				cip_d : m_cip_d,
				syt_offset : m_syt_offset
				};
			ioctl( m_fd, DV1394_INIT, &setup );
			StartThread();
		}
		result = true;
	}
	return result;
}

void* dv1394Writer::Thread()
{
	m_isRunning = true;
	while ( m_isRunning )
	{
		Frame* frame = m_deque[0];

		if ( frame )
			write( m_fd, frame->data, ( frame->IsPAL() ? DV1394_PAL_FRAME_SIZE : DV1394_NTSC_FRAME_SIZE ) );

		pthread_mutex_lock( &m_dequeMutex );
		if ( m_deque.size() > 1 )
		{
			// Discard the current frame only if more than one available
			m_deque.pop_front();
//			DoneWithFrame( frame );
			int newPos = (framePosition+1)%BUFFERED_FRAMES;
			if (frames[newPos] != NULL) {
				delete frames[newPos];
			}
			frames[newPos] = frame;
			framePosition = newPos;

			// Awaken a blocked parent thread
			TriggerAction();

			// frontmost frame has changed so we need to silence it if repeated
			m_isSilent = false;
		}
		else if ( !m_isSilent )
		{
			AudioInfo info;
			int16_t* silence[4];

			// Make the repeated frame silent to avoid annoyance
			for ( int i = 0; i < 4; i++ )
				silence[i] = (int16_t*) calloc( 2 * DV_AUDIO_MAX_SAMPLES, sizeof( int16_t ) );
			info.channels = 2;
			for ( int i = 0; i < 4; i++ )
				free( silence[i] );

			// Do not need to silence this frame again
			m_isSilent = true;
		}
		pthread_mutex_unlock( &m_dequeMutex );
	}

	return NULL;
}
#endif
